<!-- 校验数据 -->
<validator>

  <style scoped>
  .pos-right, .pos-top, .pos-bottom {
  	position: relative;
  	background: #ffc;
  	/*border: 1px solid #d48346;*/
  }
  .pos-right:after, .pos-right:before {
  	right: 100%;
  	top: 50%;
  	border: solid transparent;
  	content: " ";
  	height: 0;
  	width: 0;
  	position: absolute;
  	pointer-events: none;
  }
  .pos-right:after {
  	border-color: rgba(255, 255, 204, 0);
  	border-right-color: #ffc;
  	border-width: 3px;
  	margin-top: -3px;
  }
  .pos-right:before {
  	border-color: rgba(212, 131, 70, 0);
  	border-right-color: #d48346;
  	border-width: 4px;
  	margin-top: -4px;
  }
  .pos-top:after, .pos-top:before {
  	top: 100%;
  	left: 50%;
  	border: solid transparent;
  	content: " ";
  	height: 0;
  	width: 0;
  	position: absolute;
  	pointer-events: none;
  }
  .pos-top:after {
  	border-color: rgba(255, 255, 204, 0);
  	border-top-color: #ffc;
  	border-width: 3px;
  	margin-left: -3px;
  }
  .pos-top:before {
  	border-color: rgba(212, 131, 70, 0);
  	border-top-color: #d48346;
  	border-width: 4px;
  	margin-left: -4px;
  }
  .pos-bottom:after, .pos-bottom:before {
  	bottom: 100%;
  	left: 50%;
  	border: solid transparent;
  	content: " ";
  	height: 0;
  	width: 0;
  	position: absolute;
  	pointer-events: none;
  }

  .pos-bottom:after {
  	border-color: rgba(255, 255, 204, 0);
  	border-bottom-color: #ffc;
  	border-width: 3px;
  	margin-left: -3px;
  }
  .pos-bottom:before {
  	border-color: rgba(212, 131, 70, 0);
  	border-bottom-color: #d48346;
  	border-width: 4px;
  	margin-left: -4px;
  }
  .validator{
    color: #d48346;
    display: inline-block;
    padding: 2px 5px;
    border-radius: 3px;
  }
  .pos{
    position: absolute;
    -webkit-transition: all .2s;
    opacity: 0;
  }
  .pos.invalid{
    opacity: 1;
  }
  </style>

  <div class="pos {invalid: invalid}" ref="pos">
    <span ref="msg" show="{invalid}" class="validator pos-{opts.pos||'right'}"><yield></yield></span>
  </div>

  <script>
    /**
     * for="检查目标[ref,ref,ref,...]"
     * rule="校验规则[req,...]"
     * reg="正则规则"
     * flag="正则flag"
     * pos="显示方向[top,right,bottom]"
     * on-invalid="校验失败执行的函数(msg)"
     */
    var tag = this;
    //  默认设置为通过校验
    tag.invalid = false;
    tag.ev = {};
    tag.fn = {
      // 对指定参数初始化
      init: function(){
        if(!opts.for){
          throw 'validator 需要 for 属性来指定检查目标';
        }
        if(!opts.rule && !opts.reg){
          throw 'validator 需要 rule或reg 属性来指定校验规则';
        }
        // 信息位置定位
        var relative = opts.for.slice(-1)[0],
            pos = opts.pos||'right';
        if(pos === 'right'){
          tag.refs.pos.style.top = relative.offsetTop;
          tag.refs.pos.style.left = relative.offsetLeft + relative.offsetWidth + 10;
        }
        if(pos === 'top'){
          tag.refs.pos.style.top = relative.offsetTop - relative.offsetHeight - 7;
          tag.refs.pos.style.left = relative.offsetLeft;
        }
        if(pos === 'bottom'){
          tag.refs.pos.style.top = relative.offsetTop + relative.offsetHeight + 7;
          tag.refs.pos.style.left = relative.offsetLeft;
        }
      },
      // focus清除原来的错误信息
      clear: function(){
        opts.for.forEach(function(target){
          target.addEventListener('focus', function(){
            tag.update({
              invalid: false
            })
          }, {once: true});
        });
      },
      // 校验规则
      rules: {
        // 必填
        req: function(){
          if(tag.invalid) return;
          opts.for.forEach(function(target){
            if(target.value.replace(/\s/g, '') === ''){
              tag.invalid = true;
            }
          });
        },
        // 数字
        num: function(){
          if(tag.invalid) return;
          opts.for.forEach(function(target){
            if(Number(target.value) != target.value){
              tag.invalid = true;
            }
          });
        },
        // 正整数
        '+int': function(){
          if(tag.invalid) return;
          opts.for.forEach(function(target){
            var v = parseInt(target.value);
            if(v < 1 || !v || v != target.value){
              tag.invalid = true;
            }
          });
        },
        // 正浮.2
        '+float.2': function(){
          if(tag.invalid) return;
          opts.for.forEach(function(target){
            var v = parseFloat(target.value);
            if(!v || v != target.value || v.toFixed(2) <= 0){
              tag.invalid = true;
            }
            else if(String(v).split('.')[1] && String(v).split('.')[1].length > 2){
              tag.invalid = true;
            }
          });
        },
        // 邮箱
        mail: function(){
          if(tag.invalid) return;
          opts.for.forEach(function(target){
            if(/[^@]+@[^@]+\.[^@]+/.test(target.value) === false){
              tag.invalid = true;
            }
          });
        },
        // 两个值相等
        match: function(){
          if(tag.invalid) return;
          try{
            tag.invalid = (opts.for[0].value != opts.for[1].value);
          }
          catch(e){
            tag.invalid = true;
          }
        },
        // 正则
        reg: function(){
          if(tag.invalid) return;
          var reg = new RegExp(opts.reg, opts.flag);
          opts.for.forEach(function(target){
            tag.invalid = reg.test(target.value) === false;
          });
        }
      }
    };
    tag.on('mount', function(){
      tag.fn.init();
    });
    // 检查数据
    tag.check = function(){
      tag.invalid = false;
      try{
        opts.rule.split(',').forEach(function(rule){
          tag.fn.rules[rule]();
        });
      }
      catch(e){
        throw 'validator 校验失败：' + e.message;
      }
      if(tag.invalid){
        opts.onInvalid &&
        opts.onInvalid(tag.refs.msg.innerHTML);
        tag.fn.clear();
      }
      else{
        tag.emit('valid');
      }
      return !tag.invalid;
    };
    tag.on('check', tag.check);
  </script>

</validator>
